package com.eptok.yspay.opensdkjava.util;


import org.apache.tomcat.util.codec.binary.Base64;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.util.*;
public class CheckSignUtil {

	private final static String ALGORITHM = "SHA1WithRSA";
	private final static String CHAR_SET = "UTF-8";

	// ==============验证签名=================== //

	/**
	 * 验证签名是否正确�?
	 * 自动获取params种的sign字段
	 * @param params
	 * @param publicKeyFile
	 * @return
	 * @throws Exception
	 */
	public static boolean rsaCheckContent(Map<String, String> params, File publicKeyFile) throws Exception {
		String sign = params.get("sign");
		if (sign == null) {
			sign = "";
		}
		InputStream publicCertFileInputStream = new FileInputStream(publicKeyFile);
		String content = createLinkString(paraFilter(params));
		return rsaCheckContent(publicCertFileInputStream, content, sign, CHAR_SET);
	}

		/**
		 * 验证签名是否正确�?
		 * 已经自动去掉params种的sign字段
		 * @param params
		 * @param sign
		 * @param publicKeyFile
		 * @return   true 成功，false 失败
		 * @throws Exception
		 */
		public static boolean rsaCheckContent(Map<String, String> params, String sign,File publicKeyFile) throws Exception {
			if (sign == null) {
				sign = "";
			}
			InputStream publicCertFileInputStream = new FileInputStream(publicKeyFile);
			String content = createLinkString(paraFilter(params));
			return rsaCheckContent(publicCertFileInputStream, content, sign, CHAR_SET);
		}


		/**
		 * 验签实现�?
		 *
		 * @param publicCertFileInputStream
		 * @param content
		 * @param sign
		 * @param charset
		 * @return
		 * @throws Exception
		 */
		private static boolean rsaCheckContent(InputStream publicCertFileInputStream, String content, String sign,
				String charset) throws Exception {
			boolean bFlag = false;
			try {
				Signature signetcheck = Signature.getInstance(ALGORITHM);
				signetcheck.initVerify(getPublicKeyFromCert(publicCertFileInputStream));
				signetcheck.update(content.getBytes(charset));
				if (signetcheck.verify(Base64.decodeBase64(sign.getBytes(charset)))) {
					// 跑不进条件语句里�?
					bFlag = true;
				}
			} catch (Exception e) {
				throw new Exception("验证签名异常");
			}
			return bFlag;
		}

		/**
		 * 除去数组中的空�?�和签名参数
		 *
		 * @param sArray
		 *            签名参数�?
		 * @return 去掉空�?�与签名参数后的新签名参数组
		 */
		private static Map<String, String> paraFilter(Map<String, String> sArray) {

			Map<String, String> result = new HashMap<String, String>();

			if (sArray == null || sArray.size() <= 0) {
				return result;
			}

			for (String key : sArray.keySet()) {
				String value = sArray.get(key);
				if (value == null || StringUtil.isEmpty(value) || key.equalsIgnoreCase("sign")) {
					continue;
				}
				result.put(key, value);
			}

			return result;
		}

		/**
		 * 把数组所有元素排序，并按照�?�参�?=参数值�?�的模式用�??&”字符拼接成字符�?
		 *
		 * @param params
		 *            �?要排序并参与字符拼接的参数组
		 * @return 拼接后字符串
		 */
		private static String createLinkString(Map<String, String> params) {

			List<String> keys = new ArrayList<String>(params.keySet());
			Collections.sort(keys);

			String prestr = "";

			for (int i = 0; i < keys.size(); i++) {
				String key = keys.get(i);
				String value = params.get(key);
				if(StringUtil.isEmpty(key)||StringUtil.isEmpty(value)){
					continue;
				}
				if (i == keys.size() - 1) {
					prestr = prestr + key + "=" + value;
				} else {
					prestr = prestr + key + "=" + value + "&";
				}
			}

			return prestr;
		}

		/**
		 * 读取公钥，x509格式
		 *
		 * @param ins
		 * @return
		 * @throws Exception
		 * @see
		 */
		private static PublicKey getPublicKeyFromCert(InputStream ins) throws Exception {
			 Map<String, Object> certMap = new java.util.concurrent.ConcurrentHashMap<String, Object>();

			PublicKey pubKey = (PublicKey) certMap.get("PublicKey");
			if (pubKey != null) {
				return pubKey;
			}

			try {
				CertificateFactory cf = CertificateFactory.getInstance("X.509");
				Certificate cac = cf.generateCertificate(ins);
				pubKey = cac.getPublicKey();
				certMap.put("PublicKey", pubKey);
			} catch (Exception e) {
				if (ins != null) {
					ins.close();
				}
				throw e;
			} finally {
				if (ins != null) {
					ins.close();
				}
			}
			return pubKey;
		}
	/**
	 * gms验签实现
	 *
	 * @param params            参数
	 * @param sign              签名
	 * @param publicKeyFilePath 国密公钥地址
	 * @param charset           签名的编码方式
	 * @return
	 * @throws Exception
	 */
	public static boolean gmsM2CheckContent(Map<String, String> params, String sign, byte[] publicKeyFilePath, String charset) throws Exception {
		if (sign == null) {
			sign = "";
		}
		String content = getSignContent(params);
		byte[] bcheck = Base64.decodeBase64(sign.getBytes(charset));
		return GMSignUtils.verifyMsgSignSM2(publicKeyFilePath, bcheck, content.getBytes());
	}
	/**
	 * 遍历以及根据重新排序
	 *
	 * @param sortedParams
	 * @return
	 */
	private static String getSignContent(Map<String, String> sortedParams) {
		StringBuffer content = new StringBuffer();
		List<String> keys = new ArrayList<String>(sortedParams.keySet());
		Collections.sort(keys);
		int index = 0;
		for (int i = 0; i < keys.size(); i++) {
			String key = keys.get(i);
			String value = sortedParams.get(key);
			if (StringUtil.areNotEmpty(key, value)) {
				content.append((index == 0 ? "" : "&") + key + "=" + value);
				index++;
			}
		}
		return content.toString();
	}

}
